﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UT3Bots.Communications;

namespace UT3Bots.UTItems
{
	public class UTMap
	{
		//Private Members
		private List<UTNavPoint> _navList = new List<UTNavPoint>();
		private List<UTItemPoint> _itemList = new List<UTItemPoint>();
		//private UTBotSelfState _theBot;

		#region Properties
		public List<UTNavPoint> NavPoints
		{
			get
			{
				return this._navList;
			}
		}
		public List<UTItemPoint> InvItems
		{
			get
			{
				return this._itemList;
			}
		}
		#endregion


		//Constructor
		internal UTMap()
		{
			
		}


		internal void UpdateState(Message Message)
		{
			if (Message != null && Message.Event == EventMessage.STATE)
			{
				switch (Message.Info)
				{
                    case InfoMessage.BEGIN:
                        Clear();
                        break;
					case InfoMessage.NAV_INFO:
						UTNavPoint nav = new UTNavPoint(
							new UTIdentifier(Message.Arguments[0]),
							UTVector.Parse(Message.Arguments[1]),
							bool.Parse(Message.Arguments[2])
							);
                        lock (_navList)
                        {
                            this._navList.Add(nav);
                        }
						break;

					case InfoMessage.PICKUP_INFO:
						UTItemPoint item = new UTItemPoint(
							new UTIdentifier(Message.Arguments[0]),
							UTVector.Parse(Message.Arguments[1]),
							Message.Arguments[2],
							bool.Parse(Message.Arguments[3]),
							bool.Parse(Message.Arguments[4])
							);
                        lock (this._itemList)
                        {
                            this._itemList.Add(item);
                        }
						break;
				}
			}

		}

        internal void Clear()
        {
            lock (_itemList)
            {
                this._itemList.Clear();
            }
            lock (_navList)
            {
                this._navList.Clear();
            }
        }

		internal UTVector GetLocationFromId(string Id)
		{
			UTIdentifier tempId = new UTIdentifier(Id);
			return GetLocationFromId(tempId);
		}

		internal UTVector GetLocationFromId(UTIdentifier Id)
		{
			foreach (UTNavPoint n in this._navList)
			{
				if (n.Id == Id)
				{
					return n.Location;
				}
			}

            lock (this._itemList)
            {
                foreach (UTItemPoint i in this._itemList)
                {
                    if (i.Id == Id)
                    {
                        return i.Location;
                    }
                }
            }

			return null;
		}

		/// <summary>
		/// Returns the nearest UTNavPoint to the specified location
		/// </summary>
		/// <param name="location">The location you want to find the nearest nav point to</param>
		/// <returns>The closest UTNavPoint to that location</returns>
		public UTNavPoint GetNearestNavPoint(UTVector location)
		{
			return GetNearestNavPoint(location, null);
		}

		/// <summary>
		/// Returns the nearest UTNavPoint to the specified location, excluding the UTNavpoint specified. 
		/// Useful for ignoring the point your bot may already be stood on
		/// </summary>
		/// <param name="location">The location you want to find the nearest nav point to</param>
		/// <param name="toExclude">The UTNavPoint to ignore during the search</param>
		/// <returns>The closest UTNavPoint to that location</returns>
		public UTNavPoint GetNearestNavPoint(UTVector location, UTNavPoint toExclude)
		{
            if (location == null)
                throw new ArgumentNullException("location");
			UTNavPoint closest = null;
			float closestDistance = -1;
			foreach (UTNavPoint n in this._navList.ToList())
			{
				float myDistance = n.Location.DistanceFrom(location);
				if (n != toExclude && (myDistance < closestDistance || closestDistance < 0))
				{
					//Set as new closest
					closest = n;
					closestDistance = myDistance;
				}
			}

			return closest;
		}


		/// <summary>
		/// Returns the nearest UTItemPoint to the specified location
		/// </summary>
		/// <param name="location">The location you want to find the nearest item point to</param>
		/// <returns>The closest UTItemPoint to that location</returns>
		public UTItemPoint GetNearestItemPoint(UTVector location)
		{
			return GetNearestItemPoint(location, null);
		}

		/// <summary>
		/// Returns the nearest UTItemPoint to the specified location, excluding the UTItemPoint specified. 
		/// Useful for ignoring the point your bot may already be stood on
		/// </summary>
		/// <param name="location">The location you want to find the nearest item point to</param>
		/// <param name="toExclude">The UTItemPoint to ignore during the search</param>
		/// <returns>The closest UTItemPoint to that location</returns>
		public UTItemPoint GetNearestItemPoint(UTVector location, UTItemPoint toExclude)
		{
            if (location == null)
                throw new ArgumentNullException("location");
			UTItemPoint closest = null;
			float closestDistance = -1;
            lock (this._itemList)
            {
                foreach (UTItemPoint i in this._itemList.ToList())
                {
                    float myDistance = i.Location.DistanceFrom(location);
                    if (i != toExclude && (myDistance < closestDistance || closestDistance < 0))
                    {
                        //Set as new closest
                        closest = i;
                        closestDistance = myDistance;
                    }
                }
            }

			return closest;
		}

        #region Get Nearest Item Methods


		/// <summary>
		/// Returns the nearest UTItemPoint of a certain type to the specified location
		/// </summary>
		/// <param name="type">The type to look for</param>
		/// <param name="location">The location you want to find the nearest item point to</param>
		/// <returns>The closest UTItemPoint of that type to that location</returns>
		public UTItemPoint GetNearestItem(ItemType type, UTVector location)
		{
			return GetNearestItem(type, location, null);
		}

		/// <summary>
		/// Returns the nearest UTItemPoint of a certain type to the specified location, excluding the UTItemPoint specified.  
		/// Useful for ignoring the point your bot may already be stood on
		/// </summary>
		/// <param name="type">The type to look for</param>
		/// <param name="location">The location you want to find the nearest item point to</param>
		/// <param name="toExclude">The UTItemPoint to ignore during the search</param>
		/// <returns>The closest UTItemPoint of that type to that location</returns>
		public UTItemPoint GetNearestItem(ItemType type, UTVector location, UTItemPoint toExclude)
		{
            if (location == null)
                throw new ArgumentNullException("location");
			UTItemPoint closest = null;
			float closestDistance = -1;
            foreach (UTItemPoint i in this._itemList.ToList())
			{
				float myDistance = i.Location.DistanceFrom(location);
				if (i != toExclude && (myDistance < closestDistance || closestDistance < 0) && i.Item.IsItem(type))
				{
					//Set as new closest
					closest = i;
					closestDistance = myDistance;
				}
			}

			return closest;
		}

		/// <summary>
		/// Returns the nearest UTItemPoint of a certain type to the specified location
		/// </summary>
		/// <param name="type">The type to look for</param>
		/// <param name="location">The location you want to find the nearest item point to</param>
		/// <returns>The closest UTItemPoint of that type to that location</returns>
		public UTItemPoint GetNearestItem(WeaponType type, UTVector location)
		{
			return GetNearestItem(type, location, null);
		}

		/// <summary>
		/// Returns the nearest UTItemPoint of a certain type to the specified location, excluding the UTItemPoint specified.  
		/// Useful for ignoring the point your bot may already be stood on
		/// </summary>
		/// <param name="type">The type to look for</param>
		/// <param name="location">The location you want to find the nearest item point to</param>
		/// <param name="toExclude">The UTItemPoint to ignore during the search</param>
		/// <returns>The closest UTItemPoint of that type to that location</returns>
		public UTItemPoint GetNearestItem(WeaponType type, UTVector location, UTItemPoint toExclude)
		{
            if (location == null)
                throw new ArgumentNullException("location");
			UTItemPoint closest = null;
			float closestDistance = -1;
            foreach (UTItemPoint i in this._itemList.ToList())
			{
				float myDistance = i.Location.DistanceFrom(location);
				if (i != toExclude && (myDistance < closestDistance || closestDistance < 0) && i.Item.IsItem(type))
				{
					//Set as new closest
					closest = i;
					closestDistance = myDistance;
				}
			}

			return closest;
		}

		/// <summary>
		/// Returns the nearest UTItemPoint of a certain type to the specified location
		/// </summary>
		/// <param name="type">The type to look for</param>
		/// <param name="location">The location you want to find the nearest item point to</param>
		/// <returns>The closest UTItemPoint of that type to that location</returns>
		public UTItemPoint GetNearestItem(HealthType type, UTVector location)
		{
			return GetNearestItem(type, location, null);
		}

		/// <summary>
		/// Returns the nearest UTItemPoint of a certain type to the specified location, excluding the UTItemPoint specified.  
		/// Useful for ignoring the point your bot may already be stood on
		/// </summary>
		/// <param name="type">The type to look for</param>
		/// <param name="location">The location you want to find the nearest item point to</param>
		/// <param name="toExclude">The UTItemPoint to ignore during the search</param>
		/// <returns>The closest UTItemPoint of that type to that location</returns>
		public UTItemPoint GetNearestItem(HealthType type, UTVector location, UTItemPoint toExclude)
		{
            if (location == null)
                throw new ArgumentNullException("location");
			UTItemPoint closest = null;
			float closestDistance = -1;
            foreach (UTItemPoint i in this._itemList.ToList())
			{
				float myDistance = i.Location.DistanceFrom(location);
				if (i != toExclude && (myDistance < closestDistance || closestDistance < 0) && i.Item.IsItem(type))
				{
					//Set as new closest
					closest = i;
					closestDistance = myDistance;
				}
			}

			return closest;
		}

		/// <summary>
		/// Returns the nearest UTItemPoint of a certain type to the specified location
		/// </summary>
		/// <param name="type">The type to look for</param>
		/// <param name="location">The location you want to find the nearest item point to</param>
		/// <returns>The closest UTItemPoint of that type to that location</returns>
		public UTItemPoint GetNearestItem(AmmoType type, UTVector location)
		{
			return GetNearestItem(type, location, null);
		}

		/// <summary>
		/// Returns the nearest UTItemPoint of a certain type to the specified location, excluding the UTItemPoint specified.  
		/// Useful for ignoring the point your bot may already be stood on
		/// </summary>
		/// <param name="type">The type to look for</param>
		/// <param name="location">The location you want to find the nearest item point to</param>
		/// <param name="toExclude">The UTItemPoint to ignore during the search</param>
		/// <returns>The closest UTItemPoint of that type to that location</returns>
		public UTItemPoint GetNearestItem(AmmoType type, UTVector location, UTItemPoint toExclude)
		{
            if (location == null)
                throw new ArgumentNullException("location");
			UTItemPoint closest = null;
			float closestDistance = -1;
            foreach (UTItemPoint i in this._itemList.ToList())
			{
				float myDistance = i.Location.DistanceFrom(location);
				if (i != toExclude && (myDistance < closestDistance || closestDistance < 0) && i.Item.IsItem(type))
				{
					//Set as new closest
					closest = i;
					closestDistance = myDistance;
				}
			}

			return closest;
		}


		/// <summary>
		/// Returns the nearest UTItemPoint of a certain type to the specified location
		/// </summary>
		/// <param name="type">The type to look for</param>
		/// <param name="location">The location you want to find the nearest item point to</param>
		/// <returns>The closest UTItemPoint of that type to that location</returns>
		public UTItemPoint GetNearestItem(ArmorType type, UTVector location)
		{
			return GetNearestItem(type, location, null);
		}

		/// <summary>
		/// Returns the nearest UTItemPoint of a certain type to the specified location, excluding the UTItemPoint specified.  
		/// Useful for ignoring the point your bot may already be stood on
		/// </summary>
		/// <param name="type">The type to look for</param>
		/// <param name="location">The location you want to find the nearest item point to</param>
		/// <param name="toExclude">The UTItemPoint to ignore during the search</param>
		/// <returns>The closest UTItemPoint of that type to that location</returns>
		public UTItemPoint GetNearestItem(ArmorType type, UTVector location, UTItemPoint toExclude)
		{
            if (location == null)
                throw new ArgumentNullException("location");
			UTItemPoint closest = null;
			float closestDistance = -1;
            foreach (UTItemPoint i in this._itemList.ToList())
			{
				float myDistance = i.Location.DistanceFrom(location);
				if (i != toExclude && (myDistance < closestDistance || closestDistance < 0) && i.Item.IsItem(type))
				{
					//Set as new closest
					closest = i;
					closestDistance = myDistance;
				}
			}

			return closest;
		}


		#endregion



	}
}
